/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package com.sun.corba.se.impl.interceptors;

import java.io.IOException;

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

import java.util.HashMap;

import org.omg.PortableInterceptor.ForwardRequest;
import org.omg.PortableInterceptor.InvalidSlot;
import org.omg.PortableInterceptor.RequestInfo;
import org.omg.PortableInterceptor.LOCATION_FORWARD;
import org.omg.IOP.TaggedProfile;
import org.omg.IOP.TaggedComponent;
import org.omg.IOP.ServiceContextHelper;
import org.omg.Messaging.SYNC_WITH_TRANSPORT;
import org.omg.CORBA.ParameterMode;

import org.omg.CORBA.Any;
import org.omg.CORBA.BAD_INV_ORDER;
import org.omg.CORBA.BAD_PARAM;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.Context;
import org.omg.CORBA.ContextList;
import org.omg.CORBA.CTX_RESTRICT_SCOPE;
import org.omg.CORBA.ExceptionList;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.LocalObject;
import org.omg.CORBA.NamedValue;
import org.omg.CORBA.NO_IMPLEMENT;
import org.omg.CORBA.NO_RESOURCES;
import org.omg.CORBA.NVList;
import org.omg.CORBA.Object;
import org.omg.CORBA.Policy;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.UNKNOWN;
import org.omg.CORBA.UserException;
import org.omg.CORBA.portable.ApplicationException;
import org.omg.CORBA.portable.Delegate;
import org.omg.CORBA.portable.InputStream;

import org.omg.Dynamic.Parameter;

import com.sun.corba.se.spi.legacy.connection.Connection;

import com.sun.corba.se.spi.legacy.interceptor.RequestInfoExt;

import com.sun.corba.se.spi.ior.IOR;

import com.sun.corba.se.spi.ior.iiop.GIOPVersion;

import com.sun.corba.se.spi.orb.ORB;

import com.sun.corba.se.spi.logging.CORBALogDomains;

import com.sun.corba.se.spi.servicecontext.ServiceContexts;
import com.sun.corba.se.spi.servicecontext.UnknownServiceContext;

import com.sun.corba.se.impl.encoding.CDRInputStream_1_0;
import com.sun.corba.se.impl.encoding.EncapsOutputStream;

import com.sun.corba.se.impl.orbutil.ORBUtility;

import com.sun.corba.se.impl.util.RepositoryId;

import com.sun.corba.se.impl.logging.InterceptorsSystemException;
import com.sun.corba.se.impl.logging.OMGSystemException;

import sun.corba.SharedSecrets;

/**
 * Implementation of the RequestInfo interface as specified in
 * orbos/99-12-02 section 5.4.1.
 */
public abstract class RequestInfoImpl
    extends LocalObject
    implements RequestInfo, RequestInfoExt {
  //////////////////////////////////////////////////////////////////////
  //
  // NOTE: IF AN ATTRIBUTE IS ADDED, PLEASE UPDATE RESET();
  //
  //////////////////////////////////////////////////////////////////////

  // The ORB from which to get PICurrent and other info
  protected ORB myORB;
  protected InterceptorsSystemException wrapper;
  protected OMGSystemException stdWrapper;

  // The number of interceptors actually invoked for this client request.
  // See setFlowStackIndex for a detailed description.
  protected int flowStackIndex = 0;

  // The type of starting point call to make to the interceptors
  // See ClientRequestInfoImpl and ServerRequestInfoImpl for a list of
  // appropriate constants.
  protected int startingPointCall;

  // The type of intermediate point call to make to the interceptors
  // See ServerRequestInfoImpl for a list of appropriate constants.
  // This does not currently apply to client request interceptors but is
  // here in case intermediate points are introduced in the future.
  protected int intermediatePointCall;

  // The type of ending point call to make to the interceptors
  // See ClientRequestInfoImpl and ServerRequestInfoImpl for a list of
  // appropriate constants.
  protected int endingPointCall;

  // The reply status to return in reply_status.  This is initialized
  // to UNINITIALIZED so that we can tell if this has been set or not.
  protected short replyStatus = UNINITIALIZED;

  // Constant for an uninitizlied reply status.
  protected static final short UNINITIALIZED = -1;

  // Which points we are currently executing (so we can implement the
  // validity table).
  protected int currentExecutionPoint;
  protected static final int EXECUTION_POINT_STARTING = 0;
  protected static final int EXECUTION_POINT_INTERMEDIATE = 1;
  protected static final int EXECUTION_POINT_ENDING = 2;

  // Set to true if all interceptors have had all their points
  // executed.
  protected boolean alreadyExecuted;

  // Sources of request information
  protected Connection connection;
  protected ServiceContexts serviceContexts;

  // The ForwardRequest object if this request is being forwarded.
  // Either the forwardRequest or the forwardRequestIOR field is set.
  // When set, the other field is set to null initially.  If the other
  // field is queried, it is lazily calculated and cached.  These
  // two attributes are always kept in sync.
  protected ForwardRequest forwardRequest;
  protected IOR forwardRequestIOR;

  // PICurrent's  SlotTable
  protected SlotTable slotTable;

  // The exception to be returned by received_exception and
  // received_exception_id
  protected Exception exception;

  //////////////////////////////////////////////////////////////////////
  //
  // NOTE: IF AN ATTRIBUTE IS ADDED, PLEASE UPDATE RESET();
  //
  //////////////////////////////////////////////////////////////////////

  /**
   * Reset the info object so that it can be reused for a retry,
   * for example.
   */
  void reset() {

    // Please keep these in the same order as declared above.

    flowStackIndex = 0;
    startingPointCall = 0;
    intermediatePointCall = 0;
    endingPointCall = 0;
    // 6763340
    setReplyStatus(UNINITIALIZED);
    currentExecutionPoint = EXECUTION_POINT_STARTING;
    alreadyExecuted = false;
    connection = null;
    serviceContexts = null;
    forwardRequest = null;
    forwardRequestIOR = null;
    exception = null;

    // We don't need to reset the Slots because they are
    // already in the clean state after recieve_<point> interceptor
    // are called.
  }

    /*
     **********************************************************************
     * Access protection
     **********************************************************************/

  // Method IDs for all methods in RequestInfo.  This allows for a
  // convenient O(1) lookup for checkAccess().
  protected static final int MID_REQUEST_ID = 0;
  protected static final int MID_OPERATION = 1;
  protected static final int MID_ARGUMENTS = 2;
  protected static final int MID_EXCEPTIONS = 3;
  protected static final int MID_CONTEXTS = 4;
  protected static final int MID_OPERATION_CONTEXT = 5;
  protected static final int MID_RESULT = 6;
  protected static final int MID_RESPONSE_EXPECTED = 7;
  protected static final int MID_SYNC_SCOPE = 8;
  protected static final int MID_REPLY_STATUS = 9;
  protected static final int MID_FORWARD_REFERENCE = 10;
  protected static final int MID_GET_SLOT = 11;
  protected static final int MID_GET_REQUEST_SERVICE_CONTEXT = 12;
  protected static final int MID_GET_REPLY_SERVICE_CONTEXT = 13;
  // The last value from RequestInfo (be sure to update this):
  protected static final int MID_RI_LAST = 13;

    /*
     **********************************************************************
     * Public interfaces
     **********************************************************************/

  /**
   * Creates a new RequestInfoImpl object.
   */
  public RequestInfoImpl(ORB myORB) {
    super();

    this.myORB = myORB;
    wrapper = InterceptorsSystemException.get(myORB,
        CORBALogDomains.RPC_PROTOCOL);
    stdWrapper = OMGSystemException.get(myORB,
        CORBALogDomains.RPC_PROTOCOL);

    // Capture the current TSC and make it the RSC of this request.
    PICurrent current = (PICurrent) (myORB.getPIHandler().getPICurrent());
    slotTable = current.getSlotTable();
  }

  /**
   * Implementation for request_id() differs for client and server
   * implementations.
   *
   * Uniquely identifies an active request/reply sequence.  Once a
   * request/reply sequence is concluded this ID may be reused.  (this
   * is NOT necessarily the same as the GIOP request_id).
   */
  abstract public int request_id();

  /**
   * Implementation for operation() differs for client and server
   * implementations.
   *
   * The name of the operation being invoked.
   */
  abstract public String operation();


  /**
   * This method returns the list of arguments for the operation that was
   * invoked. It raises NO_RESOURCES exception if the operation is not invoked
   * by using DII mechanism.
   */
  abstract public Parameter[] arguments();

  /**
   * This method returns the list of exceptios  that was raised when the
   * operation was invoked. It raises NO_RESOURCES exception if the operation
   * is not invoked by using DII mechanism.
   */
  abstract public TypeCode[] exceptions();

  /**
   * This method returns the list of contexts for the DII operation.
   * It raises NO_RESOURCES exception if the operation is not invoked by
   * using DII mechanism.
   */
  abstract public String[] contexts();

  /**
   * This method returns the list of operation_context for the DII operation.
   * It raises NO_RESOURCES exception if the operation is not invoked by
   * using DII mechanism.
   */
  abstract public String[] operation_context();

  /**
   * This method returns the result from the invoked DII operation.
   * It raises NO_RESOURCES exception if the operation is not invoked by
   * using DII mechanism.
   */
  abstract public Any result();

  /**
   * Implementation for response_expected() differs for client and server
   * implementations.
   *
   * Indicates whether a response is expected.  On the client, a reply is
   * not returned when response_expected is false, so receive_reply cannot
   * be called.  receive_other is called unless an exception occurs, in
   * which case receive_exception is called.  On the client, within
   * send_poll, this attribute is true.
   */
  abstract public boolean response_expected();

  /**
   * Defined in the Messaging specification.  Pertinent only when
   * response_expected is false.  If response_expected is true, the value
   * of sync_scope is undefined.  It defines how far the request shall
   * progress before control is returned to the client.  This attribute may
   * have one of the follwing values:
   * <ul>
   * <li>Messaging::SYNC_NONE</li>
   * <li>Messaging::SYNC_WITH_TRANSPORT</li>
   * <li>Messaging::SYNC_WITH_SERVER</li>
   * <li>Messaging::SYNC_WITH_TARGET</li>
   * </ul>
   */
  public short sync_scope() {
    checkAccess(MID_SYNC_SCOPE);
    return SYNC_WITH_TRANSPORT.value; // REVISIT - get from MessageMediator
  }

  /**
   * Describes the state of the result of the operation invocation.  Its
   * value can be one of the following:
   * <ul>
   * <li>PortableInterceptor::SUCCESSFUL</li>
   * <li>PortableInterceptor::SYSTEM_EXCEPTION</li>
   * <li>PortableInterceptor::USER_EXCEPTION</li>
   * <li>PortableInterceptor::LOCATION_FORWARD</li>
   * <li>PortableInterceptor::TRANSPORT_RETRY</li>
   * </ul>
   */
  public short reply_status() {
    checkAccess(MID_REPLY_STATUS);
    return replyStatus;
  }

  /**
   * Implementation for forward_reference() differs for client and server
   * implementations.
   *
   * If the reply_status attribute is LOCATION_FORWARD
   * then this attribute will contain the object
   * to which the request will be forwarded.  It is indeterminate whether a
   * forwarded request will actually occur.
   */
  abstract public Object forward_reference();


  /**
   * Returns the data from the given slot of the PortableInterceptor::Current
   * that is in the scope of the request.
   * <p>
   * If the given slot has not been set, then an any containing a type code
   * with a TCKind value of tk_null is returned.
   * <p>
   * If the ID does not define an allocated slot, InvalidSlot is raised.
   */
  public Any get_slot(int id)
      throws InvalidSlot {
    // access is currently valid for all states:
    //checkAccess( MID_GET_SLOT );
    // Delegate the call to the slotTable which was set when RequestInfo was
    // created.
    return slotTable.get_slot(id);
  }

  /**
   * Implementation for get_request_service_context() differs for client
   * and server implementations.
   *
   * This operation returns a copy of the service context with the given ID
   * that is associated with the request.  If the request's service context
   * does not contain an etry for that ID, BAD_PARAM with a minor code of
   * TBD_BP is raised.
   */
  abstract public org.omg.IOP.ServiceContext
  get_request_service_context(int id);

  /**
   * Implementation for get_reply_service_context() differs for client
   * and server implementations.
   *
   * This operation returns a copy of the service context with the given ID
   * that is associated with the reply.  IF the request's service context
   * does not contain an entry for that ID, BAD_PARAM with a minor code of
   * TBD_BP is raised.
   */
  abstract public org.omg.IOP.ServiceContext
  get_reply_service_context(int id);

  // NOTE: When adding a method, be sure to:
  // 1. Add a MID_* constant for that method
  // 2. Call checkAccess at the start of the method
  // 3. Define entries in the validCall[][] table for interception points
  //    in both ClientRequestInfoImpl and ServerRequestInfoImpl.



    /*
     **********************************************************************
     * Proprietary methods
     **********************************************************************/

  /**
   * @return The connection on which the request is made.
   *
   * Note: we store the connection as an internal type but expose it here as an external type.
   */
  public com.sun.corba.se.spi.legacy.connection.Connection connection() {
    return connection;
  }

    /*
     **********************************************************************
     * Private utility methods
     **********************************************************************/

  /**
   * Inserts the UserException inside the given ApplicationException
   * into the given Any.  Throws an UNKNOWN with minor code
   * OMGSYstemException.UNKNOWN_USER_EXCEPTION if the Helper class could not be
   * found to insert it with.
   */
  private void insertApplicationException(ApplicationException appException,
      Any result)
      throws UNKNOWN {
    try {
      // Extract the UserException from the ApplicationException.
      // Look up class name from repository id:
      RepositoryId repId = RepositoryId.cache.getId(
          appException.getId());
      String className = repId.getClassName();

      // Find the read method on the helper class:
      String helperClassName = className + "Helper";
      Class<?> helperClass =
          SharedSecrets.getJavaCorbaAccess().loadClass(helperClassName);
      Class[] readParams = new Class[1];
      readParams[0] = org.omg.CORBA.portable.InputStream.class;
      Method readMethod = helperClass.getMethod("read", readParams);

      // Invoke the read method, passing in the input stream to
      // retrieve the user exception.  Mark and reset the stream
      // as to not disturb it.
      InputStream ueInputStream = appException.getInputStream();
      ueInputStream.mark(0);
      UserException userException = null;
      try {
        java.lang.Object[] readArguments = new java.lang.Object[1];
        readArguments[0] = ueInputStream;
        userException = (UserException) readMethod.invoke(
            null, readArguments);
      } finally {
        try {
          ueInputStream.reset();
        } catch (IOException e) {
          throw wrapper.markAndResetFailed(e);
        }
      }

      // Insert this UserException into the provided Any using the
      // helper class.
      insertUserException(userException, result);
    } catch (ClassNotFoundException e) {
      throw stdWrapper.unknownUserException(CompletionStatus.COMPLETED_MAYBE, e);
    } catch (NoSuchMethodException e) {
      throw stdWrapper.unknownUserException(CompletionStatus.COMPLETED_MAYBE, e);
    } catch (SecurityException e) {
      throw stdWrapper.unknownUserException(CompletionStatus.COMPLETED_MAYBE, e);
    } catch (IllegalAccessException e) {
      throw stdWrapper.unknownUserException(CompletionStatus.COMPLETED_MAYBE, e);
    } catch (IllegalArgumentException e) {
      throw stdWrapper.unknownUserException(CompletionStatus.COMPLETED_MAYBE, e);
    } catch (InvocationTargetException e) {
      throw stdWrapper.unknownUserException(CompletionStatus.COMPLETED_MAYBE, e);
    }
  }

  /**
   * Inserts the UserException into the given Any.
   * Throws an UNKNOWN with minor code
   * OMGSYstemException.UNKNOWN_USER_EXCEPTION if the Helper class could not be
   * found to insert it with.
   */
  private void insertUserException(UserException userException, Any result)
      throws UNKNOWN {
    try {
      // Insert this UserException into the provided Any using the
      // helper class.
      if (userException != null) {
        Class exceptionClass = userException.getClass();
        String className = exceptionClass.getName();
        String helperClassName = className + "Helper";
        Class<?> helperClass =
            SharedSecrets.getJavaCorbaAccess().loadClass(helperClassName);

        // Find insert( Any, class ) method
        Class[] insertMethodParams = new Class[2];
        insertMethodParams[0] = org.omg.CORBA.Any.class;
        insertMethodParams[1] = exceptionClass;
        Method insertMethod = helperClass.getMethod(
            "insert", insertMethodParams);

        // Call helper.insert( result, userException ):
        java.lang.Object[] insertMethodArguments =
            new java.lang.Object[2];
        insertMethodArguments[0] = result;
        insertMethodArguments[1] = userException;
        insertMethod.invoke(null, insertMethodArguments);
      }
    } catch (ClassNotFoundException e) {
      throw stdWrapper.unknownUserException(CompletionStatus.COMPLETED_MAYBE, e);
    } catch (NoSuchMethodException e) {
      throw stdWrapper.unknownUserException(CompletionStatus.COMPLETED_MAYBE, e);
    } catch (SecurityException e) {
      throw stdWrapper.unknownUserException(CompletionStatus.COMPLETED_MAYBE, e);
    } catch (IllegalAccessException e) {
      throw stdWrapper.unknownUserException(CompletionStatus.COMPLETED_MAYBE, e);
    } catch (IllegalArgumentException e) {
      throw stdWrapper.unknownUserException(CompletionStatus.COMPLETED_MAYBE, e);
    } catch (InvocationTargetException e) {
      throw stdWrapper.unknownUserException(CompletionStatus.COMPLETED_MAYBE, e);
    }
  }

    /*
     **********************************************************************
     * Protected utility methods
     **********************************************************************/

  /**
   * Internal utility method to convert an NVList into a PI Parameter[]
   */
  protected Parameter[] nvListToParameterArray(NVList parNVList) {

    // _REVISIT_ This utility method should probably be doing a deep
    // copy so interceptor can't accidentally change the arguments.

    int count = parNVList.count();
    Parameter[] plist = new Parameter[count];
    try {
      for (int i = 0; i < count; i++) {
        Parameter p = new Parameter();
        plist[i] = p;
        NamedValue nv = parNVList.item(i);
        plist[i].argument = nv.value();
        // ParameterMode spec can be found in 99-10-07.pdf
        // Section:10.5.22
        // nv.flags spec can be found in 99-10-07.pdf
        // Section 7.1.1
        // nv.flags has ARG_IN as 1, ARG_OUT as 2 and ARG_INOUT as 3
        // To convert this into enum PARAM_IN, PARAM_OUT and
        // PARAM_INOUT the value is subtracted by 1.
        plist[i].mode = ParameterMode.from_int(nv.flags() - 1);
      }
    } catch (Exception e) {
      throw wrapper.exceptionInArguments(e);
    }

    return plist;
  }

  /**
   * Utility to wrap the given Exception in an Any object and return it.
   * If the exception is a UserException which cannot be inserted into
   * an any, then this returns an Any containing the system exception
   * UNKNOWN.
   */
  protected Any exceptionToAny(Exception exception) {
    Any result = myORB.create_any();

    if (exception == null) {
      // Note: exception should never be null here since we will throw
      // a BAD_INV_ORDER if this is not called from receive_exception.
      throw wrapper.exceptionWasNull2();
    } else if (exception instanceof SystemException) {
      ORBUtility.insertSystemException(
          (SystemException) exception, result);
    } else if (exception instanceof ApplicationException) {
      // Use the Helper class for this exception to insert it into an
      // Any.
      try {
        // Insert the user exception inside the application exception
        // into the Any result:
        ApplicationException appException =
            (ApplicationException) exception;
        insertApplicationException(appException, result);
      } catch (UNKNOWN e) {
        // As per ptc/00-08-06, 21.3.13.4. if we cannot find the
        // appropriate class, then return an any containing UNKNOWN,
        // with a minor code of 1.  This is conveniently the same
        // exception that is returned from the
        // insertApplicationException utility method.
        ORBUtility.insertSystemException(e, result);
      }
    } else if (exception instanceof UserException) {
      try {
        UserException userException = (UserException) exception;
        insertUserException(userException, result);
      } catch (UNKNOWN e) {
        ORBUtility.insertSystemException(e, result);
      }
    }

    return result;
  }

  /**
   * Utility method to look up a service context with the given id and
   * convert it to an IOP.ServiceContext.  Uses the given HashMap as
   * a cache.  If not found in cache, the result is inserted in the cache.
   */
  protected org.omg.IOP.ServiceContext
  getServiceContext(HashMap cachedServiceContexts,
      ServiceContexts serviceContexts, int id) {
    org.omg.IOP.ServiceContext result = null;
    Integer integerId = new Integer(id);

    // Search cache first:
    result = (org.omg.IOP.ServiceContext)
        cachedServiceContexts.get(integerId);

    // null could normally mean that either we cached the value null
    // or it's not in the cache.  However, there is no way for us to
    // cache the value null in the following code.
    if (result == null) {
      // Not in cache.  Find it and put in cache.
      // Get the desired "core" service context.
      com.sun.corba.se.spi.servicecontext.ServiceContext context =
          serviceContexts.get(id);
      if (context == null) {
        throw stdWrapper.invalidServiceContextId();
      }

      // Convert the "core" service context to an
      // "IOP" ServiceContext by writing it to a
      // CDROutputStream and reading it back.
      EncapsOutputStream out =
          sun.corba.OutputStreamFactory.newEncapsOutputStream(myORB);

      context.write(out, GIOPVersion.V1_2);
      InputStream inputStream = out.create_input_stream();
      result = ServiceContextHelper.read(inputStream);

      cachedServiceContexts.put(integerId, result);
    }

    // Good citizen: For increased efficiency, we assume that interceptors
    // will not modify the returned ServiceContext.  Otherwise, we would
    // have to make a deep copy.

    return result;
  }


  /**
   * Utility method to add an IOP.ServiceContext to a core.ServiceContexts
   * object.  If replace is true, any service context with the given id
   * is replaced.
   * <p>
   * Raises BAD_INV_ORDER if replace is false and a service context with
   * the given id already exists.
   * <p>
   * Uses the given HashMap as a cache.  If a service context is placed
   * in the container, it goes in the HashMap as well.
   */
  protected void addServiceContext(
      HashMap cachedServiceContexts,
      ServiceContexts serviceContexts,
      org.omg.IOP.ServiceContext service_context,
      boolean replace) {
    int id = 0;
    // Convert IOP.service_context to core.ServiceContext:
    EncapsOutputStream outputStream =
        sun.corba.OutputStreamFactory.newEncapsOutputStream(myORB);
    InputStream inputStream = null;
    UnknownServiceContext coreServiceContext = null;
    ServiceContextHelper.write(outputStream, service_context);
    inputStream = outputStream.create_input_stream();

    // Constructor expects id to already have been read from stream.
    coreServiceContext = new UnknownServiceContext(
        inputStream.read_long(),
        (org.omg.CORBA_2_3.portable.InputStream) inputStream);

    id = coreServiceContext.getId();

    if (serviceContexts.get(id) != null) {
      if (replace) {
        serviceContexts.delete(id);
      } else {
        throw stdWrapper.serviceContextAddFailed(new Integer(id));
      }
    }

    serviceContexts.put(coreServiceContext);

    // Place IOP.ServiceContext in cache as well:
    cachedServiceContexts.put(new Integer(id), service_context);
  }

  /**
   * Sets the number of interceptors whose starting interception
   * points were successfully invoked on this client call.  As specified
   * in orbos/99-12-02, section 5.2.1., not all interceptors will
   * be invoked if a ForwardRequest exception or a system exception
   * is raised.  This keeps track of how many were successfully executed
   * so we know not to execute the corresponding ending interception
   * points for the interceptors whose starting interception points
   * were not completed.  This simulates the "Flow Stack Visual Model"
   * presented in section 5.1.3.
   */
  protected void setFlowStackIndex(int num) {
    this.flowStackIndex = num;
  }

  /**
   * Returns the number of interceptors whose starting interception
   * points were actually invoked on this client request.  See
   * setFlowStackIndex for more details.
   */
  protected int getFlowStackIndex() {
    return this.flowStackIndex;
  }

  /**
   * Sets which ending interception point should be called
   * for each interceptor in the virtual flow stack.
   */
  protected void setEndingPointCall(int call) {
    this.endingPointCall = call;
  }

  /**
   * Retrieves the current ending point call type (see
   * setEndingPointCall for more details).
   */
  protected int getEndingPointCall() {
    return this.endingPointCall;
  }

  /**
   * Sets which intermediate interception point should be called
   * for each interceptor in the virtual flow stack.
   */
  protected void setIntermediatePointCall(int call) {
    this.intermediatePointCall = call;
  }

  /**
   * Retrieves the current intermediate point call type (see
   * setEndingPointCall for more details).
   */
  protected int getIntermediatePointCall() {
    return this.intermediatePointCall;
  }

  /**
   * Sets which starting interception point should be called
   * for each interceptor in the virtual flow stack.
   */
  protected void setStartingPointCall(int call) {
    this.startingPointCall = call;
  }

  /**
   * Retrieves the current starting point call type (see
   * setStartingPointCall for more details).
   */
  protected int getStartingPointCall() {
    return this.startingPointCall;
  }

  /**
   * Returns true if all interceptors' starting and ending points
   * have already executed to completion, or false if not yet.
   */
  protected boolean getAlreadyExecuted() {
    return this.alreadyExecuted;
  }

  /**
   * Sets whether all interceotrs' starting and ending points
   * have already been executed to completion.
   */
  protected void setAlreadyExecuted(boolean alreadyExecuted) {
    this.alreadyExecuted = alreadyExecuted;
  }

  /**
   * Sets the value to be returned by reply_status
   */
  protected void setReplyStatus(short replyStatus) {
    this.replyStatus = replyStatus;
  }

  /**
   * Gets the current reply_status without doing an access check
   * (available only to package and subclasses)
   */
  protected short getReplyStatus() {
    return this.replyStatus;
  }

  /**
   * Stores the given ForwardRequest object for later analysis.
   * This version supplements setForwardRequest( IOR );
   */
  protected void setForwardRequest(ForwardRequest forwardRequest) {
    this.forwardRequest = forwardRequest;
    this.forwardRequestIOR = null;
  }

  /**
   * Stores the given IOR for later forward request analysis.
   * This version supplements setForwardRequest( ForwardRequest );
   */
  protected void setForwardRequest(IOR ior) {
    this.forwardRequestIOR = ior;
    this.forwardRequest = null;
  }

  /**
   * Retrieves the ForwardRequest object as a ForwardRequest exception.
   */
  protected ForwardRequest getForwardRequestException() {
    if (this.forwardRequest == null) {
      if (this.forwardRequestIOR != null) {
        // Convert the internal IOR to a forward request exception
        // by creating an object reference.
        org.omg.CORBA.Object obj = iorToObject(this.forwardRequestIOR);
        this.forwardRequest = new ForwardRequest(obj);
      }
    }

    return this.forwardRequest;
  }

  /**
   * Retrieves the IOR of the ForwardRequest exception.
   */
  protected IOR getForwardRequestIOR() {
    if (this.forwardRequestIOR == null) {
      if (this.forwardRequest != null) {
        this.forwardRequestIOR = ORBUtility.getIOR(
            this.forwardRequest.forward);
      }
    }

    return this.forwardRequestIOR;
  }

  /**
   * Sets the exception to be returned by received_exception and
   * received_exception_id.
   */
  protected void setException(Exception exception) {
    this.exception = exception;
  }

  /**
   * Returns the exception to be returned by received_exception and
   * received_exception_id.
   */
  Exception getException() {
    return this.exception;
  }

  /**
   * Sets the execution point that we are currently executing
   * (starting points, intermediate points, or ending points).
   * This allows us to enforce the validity table.
   */
  protected void setCurrentExecutionPoint(int executionPoint) {
    this.currentExecutionPoint = executionPoint;
  }

  /**
   * Check whether the caller is allowed to access this method at
   * this particular time.  This is overridden in subclasses to implement
   * the validity table specified in ptc/00-04-05, table 21-1 and 21-2.
   * The currentExecutionPoint attribute is checked, and if access is
   * forbidden at this time, BAD_INV_ORDER is raised with a minor code of
   * TBD_BIO.
   *
   * @param methodID The ID of this method, one of the MID_* constants. This allows us to easily
   * look up the method access in a table. Note that method ids may overlap between subclasses.
   */
  protected abstract void checkAccess(int methodID)
      throws BAD_INV_ORDER;

  /**
   * The server side does an explicit set rather than taking the
   * current PICurrent table as is done in the general RequestInfoImpl
   * constructor.
   */
  void setSlotTable(SlotTable slotTable) {
    this.slotTable = slotTable;
  }

  protected org.omg.CORBA.Object iorToObject(IOR ior) {
    return ORBUtility.makeObjectReference(ior);
  }
}
